package com.keta.foundation.shiro;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.stereotype.Service;

import com.keta.foundation.entity.Module;
import com.keta.foundation.entity.Organization;
import com.keta.foundation.entity.OrganizationRole;
import com.keta.foundation.entity.Permission;
import com.keta.foundation.entity.Role;
import com.keta.foundation.entity.RolePermission;
import com.keta.foundation.entity.User;
import com.keta.foundation.entity.UserRole;
import com.keta.foundation.service.OrganizationRoleService;
import com.keta.foundation.service.UserRoleService;
import com.keta.foundation.service.UserService;
import com.keta.foundation.util.Digests;
import com.keta.foundation.util.Encodes;

/*
 * 可以把Realm看做是DataSource，即安全数据源
 */
@Service
public class ShiroDbRealm extends AuthorizingRealm {

	private static final int INTERATIONS = 1024;
	private static final int SALT_SIZE = 8;
	private static final String ALGORITHM = "SHA-1";
	
	private static final Long ADMIN_ID = 1L;

	@Autowired
	private SessionDAO sessionDAO;

	private UserService userService;
	 
	private UserRoleService userRoleService;
	
	private OrganizationRoleService organizationRoleService;
	

	public ShiroDbRealm() {
		// 设置验证密码的方式，使用SHA-1加密方式
		HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(ALGORITHM);
		matcher.setHashIterations(INTERATIONS);

		setCredentialsMatcher(matcher);
	}

	/**
	 * 认证回调函数, 登录时调用.
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
			throws AuthenticationException {

		CaptchaUsernamePasswordToken token = (CaptchaUsernamePasswordToken) authcToken;

		if (token.isUseCaptcha()) {
			boolean result = CaptchaServlet.valid(SecurityUtils.getSubject().getSession().getId().toString(),
					token.getCaptcha());

			if (result == false) {
				throw new IncorrectCaptchaException("验证码错误");
			}
		}

		User user = userService.getByUsername(token.getUsername());
		if (user != null) {
			if (user.getStatus().equals(User.STATUS_DISABLED)) {
				throw new DisabledAccountException();
			}

			byte[] salt = Encodes.decodeHex(user.getSalt());

			ShiroUser shiroUser = new ShiroUser(user.getId(), user.getUsername());
			shiroUser.setUser(user);

			// 这里可以缓存认证，到研究缓存的时候来做
			return new SimpleAuthenticationInfo(shiroUser, user.getPassword(), ByteSource.Util.bytes(salt),
					this.getName());
		} else {
			return null;
		}
	}
	
	

	/*
	 * /授权回调函数
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		
		Collection<?> collection = principals.fromRealm(getName());
		if (collection == null || collection.isEmpty()) {
			return null;
		}
		
		ShiroUser shiroUser = (ShiroUser) collection.iterator().next();
		
		return newAuthorizationInfo(shiroUser);
	}
	
	
	private SimpleAuthorizationInfo newAuthorizationInfo(ShiroUser shiroUser){
		
		SimpleAuthorizationInfo info =  new SimpleAuthorizationInfo();
		
		
		User user = shiroUser.getUser();
		//重新读取最新的User对象
		user = userService.get(user.getId());
		List<Role> roles = getUserRole(user);
		
		
		if(user.getId()==ADMIN_ID){
			//超级管理员拥有所有权限
			info.addStringPermission("*");
		}else{
			for(Role role : roles){
				List<RolePermission> rolePermissions = role.getRolePermissions();
				for(RolePermission rolePermission : rolePermissions){
					Permission permission = rolePermission.getPermission();
					Module  module = permission.getModule();
					String permissionStr = module.getSn() + ":" + permission.getSn();
					info.addStringPermission(permissionStr);
				}
			}
		}
		
		
		
		return info;
	}
	
	/*
	 * 获取用户所拥有的所有角色，
	 * 包括挂在用户上面的角色 和 用户所在组织的角色 
	 */
	private List<Role> getUserRole(User user){
		
		List<Role> userRole = null;
		List<Role> organRole = null;
		userRole = user.getRoles();
		Organization org = user.getOrganization();
		organRole = org.getLogicRoles();
		
		userRole.addAll(organRole);
		
		return userRole;
	}

	public static class HashPassword {
		public String salt;
		public String password;
	}

	public static HashPassword encryptPassword(String plainPassword) {
		HashPassword result = new HashPassword();
		byte[] salt = Digests.generateSalt(SALT_SIZE);
		result.salt = Encodes.encodeHex(salt);

		byte[] hashPassword = Digests.sha1(plainPassword.getBytes(), salt, INTERATIONS);
		result.password = Encodes.encodeHex(hashPassword);
		return result;
	}

	/**
	 * 
	 * 验证密码
	 * 
	 * @param plainPassword
	 *            明文密码
	 * @param password
	 *            密文密码
	 * @param salt
	 *            盐值
	 * @return
	 */
	public static boolean validatePassword(String plainPassword, String password, String salt) {
		byte[] hashPassword = Digests.sha1(plainPassword.getBytes(), Encodes.decodeHex(salt), INTERATIONS);
		String oldPassword = Encodes.encodeHex(hashPassword);
		return password.equals(oldPassword);
	}

	/**
	 * 更新用户授权信息缓存.
	 */
	public void clearCachedAuthorizationInfo(String loginName) {
		// ShiroUser shiroUser = new ShiroUser(loginName);
		// SecurityUtils.getSubject().get
		// SecurityUtils.getSecurityManager().getSession(arg0)
		// this.get
		int a = 0;
		a = a + 1;
		Collection<Session> coll = sessionDAO.getActiveSessions();

		// SimplePrincipalCollection principals = new
		// SimplePrincipalCollection(shiroUser, getName());
		// clearCachedAuthorizationInfo(principals);
		// clearCachedAuthenticationInfo(principals);
		// this.d
		// clearCachedAuthorizationInfo(loginName);
		// SecurityManager.this.
		// get
		// SecurityUtils.getSecurityManager().logout(new );
		// this.
	}
	
	public void setSessionDAO(SessionDAO sessionDAO) {
		this.sessionDAO = sessionDAO;
	}

	public void setUserService(UserService userService) {
		this.userService = userService;
	}

	public UserRoleService getUserRoleService() {
		return userRoleService;
	}

	public void setUserRoleService(UserRoleService userRoleService) {
		this.userRoleService = userRoleService;
	}

	public OrganizationRoleService getOrganizationRoleService() {
		return organizationRoleService;
	}

	public void setOrganizationRoleService(OrganizationRoleService organizationRoleService) {
		this.organizationRoleService = organizationRoleService;
	}
}
